﻿// Copyright (c) egmkang wang. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace XServer.Grains
{
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using System.Diagnostics;
    using Orleans;
    using Orleans.Concurrency;
    using GrainInterfaces;
    using XServer.SDK;
    using XServer.Message;
    using SDK.Net;

    enum PlayerLoadState
    {
        UNLOAD,
        LOAD_COMPLETE,
        CREATE_PLAYER,
    }

    public class PlayerGrain : Grain, IPlayerGrain
    {
        static Task<ResultID> ResultOK = Task.FromResult(ResultID.ErrOk);
        static Stopwatch stopWatch = new Stopwatch();

        #region MessageCallBack
        public delegate Task<ResultID> CallBack(PlayerGrain grain, IMessage message);
        static OnceFlag once = new OnceFlag();
        static Dictionary<ushort, CallBack> Methods = new Dictionary<ushort, CallBack>();

        static void InitMessageCallback()
        {
            var methods = typeof(PlayerGrain).GetMethods();
            foreach (var method in methods)
            {
                var attributes = method.GetCustomAttributes(typeof(MessageCallbackAttribute), false);
                if (attributes.Length != 1)
                {
                    continue;
                }
                var attribute = attributes[0] as MessageCallbackAttribute;
                Methods.Add(attribute.msgid, (CallBack)method.CreateDelegate(typeof(CallBack)));
                LoggerProvider.Logger.Info("Register PlayerGrain MessageCallBack: {0:X}", attribute.msgid);
            }
        }

        #endregion

        #region PlayerStateBegin
        volatile PlayerLoadState loadStatus = PlayerLoadState.UNLOAD;
        string openid = null;
        string gatewayKey = null;
        IGatewayObserver ob = null;
        #endregion

        public long UID => this.GetPrimaryKeyLong();
        public bool LoadComplete => this.loadStatus == PlayerLoadState.LOAD_COMPLETE;

        #region Send Messages
        Task SendMessageToClient(IMessage msg)
        {
            LoggerProvider.Logger.Info("SendMessageToClient, 0x{0:X}", msg.GetMsgID());
            this.SendMessage((int)DestType.DestType_Player, this.UID, new Immutable<byte[]>(msg.ToBytes()));
            return TaskDone.Done;
        }
        #endregion

        public override Task OnActivateAsync()
        {
            return base.OnActivateAsync();
        }

        public override Task OnDeactivateAsync()
        {

            return base.OnDeactivateAsync();
        }

        public Task<FriendInfo> GetFrindInfo()
        {
            throw new NotImplementedException();
        }

        public Task<OtherPlayerInfo> GetOtherPlayerInfo()
        {
            throw new NotImplementedException();
        }

        public Task<PlayerSimpleInfo> GetSimpleInfo()
        {
            throw new NotImplementedException();
        }

        public Task LoadAsync()
        {
            //TODO:egmkang


            this.loadStatus = PlayerLoadState.LOAD_COMPLETE;
            return TaskDone.Done;
        }

        public async Task RecvMessage(ushort msgid, Immutable<byte[]> buffer)
        {
            if (Profile.Enbale)
            {
                stopWatch.Restart();
            }
            var message = null as IMessage;
            if (buffer.Value.Length > 0)
            {
                message = Codec.NewMessage(msgid, buffer.Value);
            }

            var callback = null as CallBack;
            if (Methods.TryGetValue(msgid, out callback))
            {
                var resultId = await callback(this, message);
                if (resultId != ResultID.ErrOk)
                {
                    var errorCode = new MsgNotifyError();
                    errorCode.MsgId = msgid;
                    errorCode.ErrCode = (int)resultId;
                    await this.SendMessageToClient(errorCode);
                }
            }
            else
            {
                LoggerProvider.Logger.Error("Player:{0} MessageName:{1} not find callback", this.UID, msgid);
            }
            if (Profile.Enbale)
            {
                stopWatch.Stop();
                Profile.Update(msgid, stopWatch.ElapsedTicks);
            }

            await TaskDone.Done;
        }

        public Task RegisterObserver(string key, IGatewayObserver ob)
        {
            this.gatewayKey = key;
            this.ob = ob;
            return TaskDone.Done;
        }

        public Task SendMessage(int destType, long dest, Immutable<byte[]> buffer)
        {
            if (this.ob == null)
            {
                LoggerProvider.Logger.Error("SendMessage, GatewayObserver is NULL");
                return TaskDone.Done;
            }
            this.ob.OnRecvMessage(destType, dest, buffer);
            return TaskDone.Done;
        }

        public Task UnRegisterObserver(string key)
        {
            this.gatewayKey = null;
            this.ob = null;
            return TaskDone.Done;
        }

        async Task SendAllDataToClient()
        {
            //TODO:egmkang
            await TaskDone.Done;
        }

        #region Process Player Message

        [MessageCallback(MsgID.MSG_REQ_LOGIN)]
        async Task<ResultID> ProcessPlayerLogin(IMessage msg)
        {
            var message = msg as MsgReqLogin;
            if (!this.LoadComplete)
            {
                await this.LoadAsync();
            }

            var response = new MsgRspLogin();
            response.OpenID = message.OpenID;
            response.PlayerID = this.UID;
            response.ExpiryTime = Platform.GetSeconds();
            //TODO:egmkang
            //Token
            response.Token = "";


            return await ResultOK;
        }

        [MessageCallback(MsgID.MSG_REQ_LOAD_PLAYER)]
        async Task<ResultID> ProcessLoadPlayer(IMessage msg)
        {
            if (this.loadStatus == PlayerLoadState.UNLOAD)
            {
                return ResultID.ErrInternal;
            }
            if (this.loadStatus == PlayerLoadState.CREATE_PLAYER)
            {
                return ResultID.ErrPlayerNeedCreate;
            }

            await this.SendAllDataToClient();
            return await ResultOK;
        }

        #endregion

    }
}
